Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(attachments, storage): u#3727 t#3961 physical attachment deletion system #476

Merged
merged 1 commit into from
Sep 8, 2023

Conversation

bameda
Copy link
Contributor

@bameda bameda commented Sep 4, 2023

This PR ensures that the files are deleted when the attachments are deleted. Several things must be taken into consideration:

  • Currently, files are saved on disk, but this will change so we will have to use django's storage module.
  • In the future we will have to postpone the deletion and make it easily recoverable (to avoid data loss). This is in line with the logical deletion of objects.

The solution that has been reached consists of:

  • A new module taiga.commons.storage where everything related to file storage will be created.
  • A new StoragedObject model that will store the information about where the file is stored.
  • Now the Attachment model will have a ForeingKey to StoragedObject
  • A signal is defined for Attachment during the post_save that will mark the associated StoragedObject as deleted.
  • A periodic task is created that will physically delete all files that belong to StoragedObject records that have been deleted more than X days ago. (The frequency of the periodic task and time that the files will be kept are configurable parameters).
  • There is a CLI command to remove deleted StoragedObject items and their files.

How to tests:

  • Review the code.
  • Tests are green.
  • Manual tests: Tests logical deletion:
    • Init:
      • 👸 Open the backend admin panel and access to Storaged Objects list view
      • 👤 Open the frontend
    • Tests delete attachment:
      • 👤 Go to a story and upload some attachments
      • 👸 refresh and see new storaged objets created
      • 👤 Delete attachments
      • 👸 refresh and see storaged objets with their deleted datetime.
    • Tests delete project:
      • 👤 Create a new project.
      • 👤 Create a new story and upload some attachments (you can create more than one story with attachments)
      • 👸 refresh and see new storaged objets created
      • 👤 Delete the project
      • 👸 refresh and see all the storaged objets with their deleted datetime.
    • [Optional] Tests delete user with workspaces and projects with stories with atachments:
      • You can test a more complex scenario: deleting a user involves deleting workspaces that have projects with stories "full" of attachments.
  • Manual tests: Tests physical deletion:
    • Test CLI: python -m taiga storage clean-storaged-objects --help
    • Test periodic task: You can modify TAIGA_STORAGE__DAYS_TO_STORE_DELETED_STORAGED_OBJECTS (int or None for all) and TAIGA_STORAGE__DAYS_TO_STORE_DELETED_STORAGED_OBJECTS (use cron format)

@bameda bameda marked this pull request as draft September 4, 2023 08:36
@github-actions
Copy link

github-actions bot commented Sep 4, 2023

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/taiga
   __main__.py71710%17–155
   wsgi.py15150%8–31
src/taiga/attachments
   admin.py27390%25, 28, 70
   events.py8275%15, 20
   models.py19289%60, 63
   repositories.py49293%62, 64
src/taiga/auth/services
   __init__.py47098%90–>93
src/taiga/base
   mocks.py741280%93–94, 107–118, 143–>exit
src/taiga/base/api
   permissions.py49199%22
   requests.py11380%15, 21–23
src/taiga/base/db
   __init__.py4175%15
   commands.py54540%8–271
   sequences.py45297%91–92
src/taiga/base/db/admin
   forms.py14838%17–25
   utils.py11736%17–23
src/taiga/base/db/models
   fields.py15188%20
src/taiga/base/django
   __init__.py5260%17–19
   settings.py30293%16–17
   urls.py550%8–13
   wsgi.py440%8–14
src/taiga/base/i18n
   __init__.py102988%171–>170, 173–>170, 192–193, 203–205, 215, 268, 279, 283
   choices.py34291%80, 87
   commands.py74740%8–108
src/taiga/base/logging
   formatters.py241050%22–>exit, 38, 44–45, 51–60
   middlewares.py23196%20
src/taiga/base/repositories
   neighbors.py39391%43–44, 65
src/taiga/base/sampledata
   commands.py11110%8–40
   constants.py15150%14–51
   demo_data.py2732730%8–529
   factories.py1221220%9–353
   test_data.py2902900%8–634
src/taiga/base/serializers
   fields.py20196%21
src/taiga/base/utils
   commands.py31310%8–60
   datetime.py441860%54, 67, 126–147
   enum.py14192%43
   images.py26387%21–22, 30
   json.py331545%30–35, 37, 39–44, 46, 50
   pprint.py660%8–17
   slug.py23485%22–25
src/taiga/base/validators/fields
   i18n.py16291%19–20
   uuid.py15290%19–20
src/taiga/comments
   admin.py29392%16, 20, 24
   events.py11373%15, 20, 25
   models.py20291%43, 46
src/taiga/commons/storage
   commands.py13130%8–40
   models.py16288%38, 41
   services.py9092%17–>16
   tasks.py990%8–19
src/taiga/conf
   __init__.py93395%109, 125–126
   tokens.py17190%34
src/taiga/emails
   commands.py35350%9–68
   sender.py18571%48–50, 54–73
   tasks.py39490%44–45, 53–54
src/taiga/events
   app.py18192%21
   events.py16288%31–32
   logging.py392331%18–>exit, 32–48, 51–60
   manager.py1171190%78–79, 223–233, 239–242
   responses.py33684%33, 37–41
   subscriber.py80495%51, 55, 88, 91, 108–>120
src/taiga/events/actions
   auth.py231144%27–32, 39–45
   event_handlers.py9456%18–20, 28–30
   projects.py553038%36–40, 48–64, 72–80, 88–96
   workspaces.py553038%36–40, 48–64, 72–80, 88–96
src/taiga/events/pubsub/backends
   base.py30198%53
   redis.py66594%41–43, 85–86
src/taiga/exceptions/api
   __init__.py27097%28–>30
   handlers.py27290%27–28
   middlewares.py26287%37, 41
src/taiga/integrations/auth
   services.py39782%50–56
src/taiga/integrations/github
   services.py40098%63–>68
src/taiga/mediafiles
   admin.py20291%24, 27
   models.py26292%84, 87
src/taiga/permissions
   services.py103198%28, 106–>109
src/taiga/permissions/validators
   fields.py24388%18–19, 45
src/taiga/projects/invitations
   models.py25292%89, 92
   repositories.py84198%135
src/taiga/projects/invitations/api
   validators.py65789%38–40, 65–66, 84–85
src/taiga/projects/invitations/events
   __init__.py33191%37–>exit, 49–>exit, 74–>exit, 102
src/taiga/projects/invitations/services
   __init__.py183199%58–>53, 286
src/taiga/projects/memberships
   models.py16288%51, 54
   repositories.py86297%91, 124
src/taiga/projects/memberships/events
   __init__.py16288%18–24
src/taiga/projects/memberships/services
   __init__.py45195%64–>69, 111
src/taiga/projects/projects
   admin.py531275%33–34, 39–42, 50–51, 56–59, 90, 94
   models.py60886%64, 67, 78, 112, 115, 118–120
   repositories.py115496%125, 126–>129, 155, 254–255
   tasks.py6188%17
src/taiga/projects/projects/services
   __init__.py105592%77, 86, 192, 198–>201, 206, 228
src/taiga/projects/references
   mixins.py13093%25–>28
src/taiga/projects/roles
   api.py35393%46–48
   models.py26386%50, 53, 57
   repositories.py44196%92
src/taiga/projects/roles/services
   __init__.py25194%24, 57–>62
src/taiga/stories/assignments
   models.py15287%42, 45
   repositories.py52197%49
src/taiga/stories/attachments
   api.py42196%128
src/taiga/stories/comments
   api.py58197%177
src/taiga/stories/stories
   admin.py25584%76–81, 85, 89
   models.py25292%73, 76
src/taiga/stories/stories/services
   __init__.py113297%233, 256
src/taiga/tasksqueue
   app.py20286%29–30
   commands.py65650%8–122
   logging.py331740%18–>exit, 29–39, 42–51
   manager.py561377%37, 41, 45, 49, 94, 163, 177, 186, 191, 198, 202, 209, 213
   task.py12192%23
src/taiga/tokens
   admin.py441081%67, 70, 73, 76, 104, 108, 112, 116, 120, 124
   models.py30487%59, 62, 78, 81
src/taiga/users
   admin.py51781%32, 41, 50, 87–95
   commands.py990%8–26
   models.py651086%71, 74, 90, 93, 97, 100, 103, 106, 127, 130
   repositories.py204398%209–>212, 335, 428–429
src/taiga/users/api
   __init__.py66296%216, 304
src/taiga/users/api/validators
   __init__.py42392%39–41
src/taiga/users/services
   __init__.py244795%75, 259, 262–>257, 297–>280, 306–>280, 322–>318, 345–>341, 366, 494–>499, 513–520, 521–>526, 524–525
src/taiga/workflows
   models.py36489%39, 42, 72, 75
src/taiga/workflows/services
   __init__.py91394%171, 198, 269–>277, 284
src/taiga/workspaces/invitations
   models.py24292%84, 87
   permissions.py9182%19
   repositories.py77198%123
src/taiga/workspaces/invitations/api
   validators.py18288%24–25
src/taiga/workspaces/invitations/events
   __init__.py25191%36–>exit, 60–>exit, 71
src/taiga/workspaces/invitations/services
   __init__.py123199%222
src/taiga/workspaces/memberships
   api.py43196%126
   models.py15287%43, 46
   repositories.py63295%72, 105
src/taiga/workspaces/memberships/services
   __init__.py46197%117
src/taiga/workspaces/workspaces
   admin.py571962%25–26, 31–34, 49, 52–57, 61, 65, 73–74, 79–82
   models.py18290%32, 35
   repositories.py127299%232–233
src/taiga/workspaces/workspaces/services
   __init__.py54294%113, 116
TOTAL9779158782% 

Tests Skipped Failures Errors Time
1125 1 💤 0 ❌ 0 🔥 23m 9s ⏱️

@bameda bameda force-pushed the us/3727/back_clean_deleted_attachments branch 8 times, most recently from 8841c36 to 67b776c Compare September 5, 2023 16:09
@bameda bameda changed the title feat(attachments, store): u#3727 t#3961 physical attachment deletion system. feat(attachments, store): u#3727 t#3961 physical attachment file deletion system. Sep 5, 2023
@bameda bameda force-pushed the us/3727/back_clean_deleted_attachments branch 4 times, most recently from a226a7a to 449e660 Compare September 6, 2023 18:37
@bameda bameda marked this pull request as ready for review September 6, 2023 18:41
@bameda bameda force-pushed the us/3727/back_clean_deleted_attachments branch from 449e660 to d37d442 Compare September 6, 2023 19:34
@bameda bameda changed the title feat(attachments, store): u#3727 t#3961 physical attachment file deletion system. feat(attachments, storage): u#3727 t#3961 physical attachment deletion system Sep 6, 2023
@bameda bameda force-pushed the us/3727/back_clean_deleted_attachments branch 2 times, most recently from d772072 to ff3f953 Compare September 6, 2023 21:07
@daniel-herrero daniel-herrero self-assigned this Sep 7, 2023
@bameda bameda force-pushed the us/3727/back_clean_deleted_attachments branch 2 times, most recently from 94bb974 to dde6bf1 Compare September 8, 2023 09:58
@admin.register(StoragedObject)
class StoragedObjectAdmin(admin.ModelAdmin[StoragedObject]):
list_display = (
"id" "file",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You missed a comma

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ouch

@bameda bameda force-pushed the us/3727/back_clean_deleted_attachments branch from dde6bf1 to 3547390 Compare September 8, 2023 10:37
Copy link
Contributor

@daniel-herrero daniel-herrero left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mega clap!

@bameda bameda merged commit 9028144 into main Sep 8, 2023
2 checks passed
@bameda bameda deleted the us/3727/back_clean_deleted_attachments branch September 8, 2023 11:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants